package com.chenfan.ccp.plug.log.handler;

import ch.qos.logback.core.rolling.RollingFileAppender;
import com.chenfan.ccp.util.start.ApplicationContextUtil;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class CustomRollingFileAppender extends RollingFileAppender {

    private static final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() >> 1);
    private static final Map<String, CustomRollingFileAppender> appenderMap = new ConcurrentHashMap();
    private static final Map<String, Set<PosixFilePermission>> ORIGINAL_PERMISSION = new ConcurrentHashMap<>();
    private static final Pattern filePattern = Pattern.compile("(\\d+)?(\\.log|\\.log\\.zip)$");
    private static final int MAX_JVM_FILE_COUNT = Integer.MAX_VALUE;
    public volatile static String LOG_SUFFIX = "";

    static {

        scheduled.scheduleWithFixedDelay(new TimerTask() {
            @Override
            public void run() {
                //保持文件删除还能继续
                CustomRollingFileAppender.appenderMap.keySet().forEach((x) -> {

                    if (!Files.exists(Paths.get(x)) && CustomRollingFileAppender.appenderMap.get(x).isStarted()) {
                        try {
                            CustomRollingFileAppender.appenderMap.get(x).openFile(x);
                        } catch (IOException var2) {
                        }
                    }

                });


            }
        }, 1L, 1L, TimeUnit.MINUTES);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            //定时任务
            scheduled.shutdown();

            if (!ApplicationContextUtil.isWindows() && !ApplicationContextUtil.isProd()) {
                //复原权限
                for (Map.Entry<String, Set<PosixFilePermission>> entry : ORIGINAL_PERMISSION.entrySet()) {
                    PosixFileAttributeView posixView = Files.getFileAttributeView(new File(entry.getKey()).toPath(),
                            PosixFileAttributeView.class);
                    if (posixView != null) {
                        try {
                            posixView.setPermissions(entry.getValue());
                        } catch (IOException e) {

                        }
                    }
                }
            }


        }));


    }

    private String logFileName = "";

    public CustomRollingFileAppender() {

        scheduled.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    CustomRollingFileAppender.appenderMap.put(CustomRollingFileAppender.this.getFile(), CustomRollingFileAppender.this);
                } catch (Exception var2) {
                }

            }
        }, 30L, TimeUnit.SECONDS);


    }


    @Override
    public void openFile(String file_name) throws IOException {
        super.openFile(file_name);


        if (!ApplicationContextUtil.isProd() || ApplicationContextUtil.isWindows()) {
            return;
        }

        File file = new File(file_name);
        if (!file.exists()) {
            return;
        }

        //new permission
        PosixFileAttributeView posixView = Files.getFileAttributeView(file.toPath(),
                PosixFileAttributeView.class);
        Set<PosixFilePermission> oldPermission = posixView.readAttributes().permissions();
        EnumSet<PosixFilePermission> allFilePermission = EnumSet.allOf(PosixFilePermission.class);

        writeNewPermission(posixView, oldPermission, allFilePermission);
    }

    private String getMultipleFiles(String fileName) {

        try {

            //初次不会进入
            if (!new File(fileName).exists()
                    || ApplicationContextUtil.isWindows() || !ApplicationContextUtil.isProd()) {
                return fileName;
            }

            Matcher fileMatcher = filePattern.matcher(fileName);

            if (!fileMatcher.find()) {
                return fileName;
            }


            File file;
            EnumSet<PosixFilePermission> allFilePermission = EnumSet.allOf(PosixFilePermission.class);
            PosixFileAttributeView posixView;
            Set<PosixFilePermission> oldPermission = null;
            String filePrefix = fileName.substring(0, fileName.lastIndexOf(fileMatcher.group()));
            String tmpFileName = filePrefix + (fileMatcher.group(1) == null ? "" : fileMatcher.group(1)) + fileMatcher.group(2);

            int filIndex = fileMatcher.group(1) == null ? 0 : Integer.parseInt(fileMatcher.group(1));
            //校验权限
            for (int i = filIndex; i < MAX_JVM_FILE_COUNT; i++) {

                file = new File(tmpFileName);

                if (file.exists()) {
                    posixView = Files.getFileAttributeView(file.toPath(),
                            PosixFileAttributeView.class);
                    oldPermission = posixView.readAttributes()
                            .permissions();
                }

                if (file.exists() && oldPermission.size() != allFilePermission.size()
                        || !file.exists()) {
                    logFileName = tmpFileName;

                    Matcher matcher = filePattern.matcher(logFileName);
                    //增加压缩文件的后缀
                    if (matcher.find() && matcher.group(1) != null && LOG_SUFFIX.isEmpty()) {
                        LOG_SUFFIX = matcher.group(1);
                    }

                    break;
                }


                //寻找可用文件
                tmpFileName = filePrefix + i + fileMatcher.group(2);


            }


            if (logFileName.isEmpty()) {

                throw new RuntimeException(String.format("超出单机最大%d个JVM同日志进程", MAX_JVM_FILE_COUNT));
            }


        } catch (IOException e) {

        }
        return logFileName;

    }


    private void writeNewPermission(PosixFileAttributeView posixView, Set<PosixFilePermission> oldPermission,
                                    Set<PosixFilePermission> newPermission) throws IOException {
        ORIGINAL_PERMISSION.put(getFile(), oldPermission);

        posixView.setPermissions(newPermission);
    }

    @Override
    public String getFile() {
        return logFileName.isEmpty() ? super.getFile() : logFileName;
    }

    @Override
    public void setFile(String fileName) {
        String multipleFiles = getMultipleFiles(fileName);

        super.setFile(multipleFiles);

    }

}
